#include <stdlib.h>
#include <stdio.h>
#include "mpi.h"
#include "tokenize.h"
#include "userinput.h"

unsigned long long hard_vector[64];
int vector_size;

int collisions;
unsigned long long * possibilities;
int p_size;

FILE *vptr;
FILE *cptr;

inline unsigned long long * getArray(int numULL)
{
	return (unsigned long long *)malloc(sizeof(unsigned long long) * numULL);
}

//adds a possibility for the current token to the array of possibilities
//if adding would overflow the array then double the size of the array
//copy over, free the old array and set the old arrays pointer to the new
//array
void addPossibility(unsigned long long poss)
{
	if(collisions + 1 > p_size)
	{
		unsigned long long * ptr = getArray(p_size * 2);
		int i;
		for(i=0; i < p_size; i++)
		{
			ptr[i] = possibilities[i];
		}
		free(possibilities);
		possibilities = ptr;
		p_size *= 2;
	}
	
	possibilities[collisions] = poss;
	collisions++;
}

//prints out the number chars from a possibility bit string
//the bits will be in an unsigned long long so we need to take
//them out 8 at a time
void printChars(int index)
{
	//since we are constraining the size of the vector to multiples
	//of 8 we cand do this division
	int numChars = vector_size / 8;
	int i;
	unsigned long long poss = possibilities[index];
	
	for(i = 0; i < numChars; i++)
	{
		//(numChars - i - 1) * 8 is how many bits right the unsigned long long
		//needs to be shifted to put the current character into the bottom 8 bits
		printf("%c", (poss >> ((numChars - i - 1) * 8)) & 0xFF);
	}
}

void printVec()
{
	int i;
	for(i=0;i<vector_size;i++){printf("%llu ", hard_vector[i]);}
	printf("\n");
}

int main(int argc, char *argv[])
{

	unsigned long long vec_value;
	unsigned long long token;
	int i,j;
	int numprocs, rank;
	cptr = (FILE *)getCipherFileFromUser();
	vptr = (FILE *)getVectorFileFromUser();
	
	MPI_Init(&argc,&argv);
	MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
	MPI_Comm_rank(MPI_COMM_WORLD, &rank);
	

	//initialize the hard_vector
	for(i = 0; i < 64, (vec_value = getNextToken(vptr)) != 0; i++)
	{
		hard_vector[i] = vec_value;
		vector_size++;
	}
	
	//initialize possibilities array
	possibilities = getArray(10);
	p_size = 10;
	collisions = 0;
	
	//loop over all tokens in cipher text
	while((token = getNextToken(cptr)) != 0)
	{
		unsigned long long loops = 1 << vector_size;
		
		//here we are actually checking all possibilities
		//each i corresponds to one bit mask for the hard vector
		for(i = 0; i < loops; i++)
		{
			unsigned long long sum = 0;
			
			//loop over the bits of i checking if they are set or not
			//if they are add the corresponding element from the hard
			//vector to the sum
			for(j = 0; j < vector_size; j++)
			{
				if((1 << j) & i)
				{
					sum += hard_vector[vector_size - j - 1];
				}
			}
			
			//we now have the sum for the elements in the hard vector
			//corresponding to this i
			if(token == sum)
			{
				addPossibility(i);
			}
		}
		
		//we now have all the possible bit masks (as unsigned long longs) that
		//the sum of the elements in the hard vector that each of the bits
		//corresponds to equals the current token of the cipher text.
		//we could try and decide which one to print.  However, we wont.
		//we will print out the possibilities
		
		if(collisions > 1)
		{
			printf("[ ");
			for(i = 0; i < collisions; i++)
			{
				printChars(i);
				if (i != collisions -1)
				{
					printf(", ");
				}
				else
				{
					printf(" ");
				}
			}
			printf("]");
		}
		else
		{
			printf("%c", (char)possibilities[0]);
		}
		
		collisions = 0;
	}
	
	printf("\n");
	MPI_Finalize();
	return 0;
}
